{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "dc4c1354-bf95-4ad6-9197-64cac3fa1111",
   "metadata": {},
   "source": [
    "# Widgets Tests\n",
    "\n",
    "Tests various widgets in package `github.com/janpfeifer/gonb/gonbui/widgets`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "82c87035-c921-4175-adee-176fb3ea2568",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "%goflags=[\"--cover\" \"--covermode=set\"]\n"
     ]
    }
   ],
   "source": [
    "%goflags --cover --covermode=set"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "991075d2-cb0e-4c17-97ec-64174d69b9df",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ok\n"
     ]
    }
   ],
   "source": [
    "!if [[ \"${GONB_GIT_ROOT}\" == \"\" ]] ; then \\\n",
    "    echo \"Please set GONB_GIT_ROOT before runnig this notebook!\" 1>&2 ; \\\n",
    "else \\\n",
    "    echo \"ok\" ; \\\n",
    "fi"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "79012c7d-ff26-4dc8-b9f5-684e09d49039",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\t- Added replace rule for module \"github.com/janpfeifer/gonb\" to local directory \"/home/janpf/Projects/gonb\".\n"
     ]
    }
   ],
   "source": [
    "!*rm -f go.work && go work init && go work use . \"${GONB_GIT_ROOT}\"\n",
    "%goworkfix"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a2ecd95-60f6-4c65-8117-662220a250e1",
   "metadata": {},
   "source": [
    "### Buttons\n",
    "\n",
    "Create a button and induce a click on it using Javacript.\n",
    "It should print out \"ok\" if the button was clicked or \"error\" if it times out.\n",
    "\n",
    "Notice to avoid the race condition of the button being clicked before we start listening to\n",
    "it, we add a buffer of 1 to the channel.\n",
    "\n",
    "We also create a transient div, and append the button there -- making the button not appear after it is saved.\n",
    "No real need for that, other than to also test the `AppendTo` method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "0bc8019a-ee78-4e9a-a603-3de6b5915b0b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div id=\"dom.transient_div_229bb24f\"></div>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": []
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": []
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "clicked\n"
     ]
    }
   ],
   "source": [
    "import (\n",
    "    \"github.com/janpfeifer/gonb/gonbui\"\n",
    "    \"github.com/janpfeifer/gonb/gonbui/dom\"\n",
    "    \"github.com/janpfeifer/gonb/gonbui/widgets\"\n",
    ")\n",
    "\n",
    "%%\n",
    "// Setting these 3 values below are not needed, we just do it to test they work.\n",
    "htmlId := \"gonb_widget_test_\" + gonbui.UniqueId()\n",
    "rootId := dom.CreateTransientDiv()\n",
    "address := \"widget/\" + gonbui.UniqueId()\n",
    "\n",
    "w := widgets.Button(\"Ok\").\n",
    "    WithAddress(address).WithHtmlId(htmlId).AppendTo(rootId).Done()\n",
    "wChan := w.Listen().WithBuffer(1)\n",
    "if w.HtmlId() != htmlId {\n",
    "    fmt.Println(\"error: wrong htmlId\")\n",
    "    return\n",
    "}\n",
    "if w.Address() != address {\n",
    "    fmt.Println(\"error: wrong address\")\n",
    "    return\n",
    "}\n",
    "\n",
    "// Click on button with Javascript after 100ms.\n",
    "go func() {\n",
    "    time.Sleep(100 * time.Millisecond);\n",
    "    dom.TransientJavascript(fmt.Sprintf(`\n",
    "(() => {\n",
    "    let button = document.getElementById(\"%s\");\n",
    "    button.click();\n",
    "    console.log(\"Clicked!\");\n",
    "})();\n",
    "`, w.HtmlId()))\n",
    "}()\n",
    "\n",
    "// Wait for button click or timeout.\n",
    "select {\n",
    "case <-wChan.C:\n",
    "    fmt.Println(\"clicked\")\n",
    "case <-time.After(500 * time.Millisecond):\n",
    "    fmt.Println(\"error\")\n",
    "}\n",
    "wChan.Close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "56c9e305-5e67-4c12-8e28-d5c5ef6623c0",
   "metadata": {},
   "source": [
    "### Sliders\n",
    "\n",
    "We create a slider, and then interact with it with Javascript and test we get the correct reading."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "c81a91e5-f392-4def-b861-a50aa6ca0734",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div id=\"dom.transient_div_200a17df\"></div>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": []
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "widget tested ok\n"
     ]
    }
   ],
   "source": [
    "%%\n",
    "const answer = 42\n",
    "\n",
    "// Setting these 3 values below are not needed, we just do it to test they work.\n",
    "htmlId := \"gonb_widget_test_\" + gonbui.UniqueId()\n",
    "rootId := dom.CreateTransientDiv()\n",
    "address := \"widget/\" + gonbui.UniqueId()\n",
    "\n",
    "w := widgets.Slider(0, 100, 50).\n",
    "    WithAddress(address).WithHtmlId(htmlId).AppendTo(rootId).Done()\n",
    "wChan := w.Listen().WithBuffer(1)\n",
    "if w.HtmlId() != htmlId {\n",
    "    fmt.Println(\"error: wrong htmlId\")\n",
    "    return\n",
    "}\n",
    "if w.Address() != address {\n",
    "    fmt.Println(\"error: wrong address\")\n",
    "    return\n",
    "}\n",
    "\n",
    "go func() {\n",
    "    time.Sleep(100 * time.Millisecond);\n",
    "    dom.TransientJavascript(fmt.Sprintf(`\n",
    "(() => {\n",
    "    let el = document.getElementById(\"%s\");\n",
    "    const answer = %d;\n",
    "    el.value = answer;  // Doesn't trigger \"change\" event, unfortunately.\n",
    "    el.dispatchEvent(new Event(\"change\"));\n",
    "    console.log(\"Select set to \"+answer);\n",
    "})();\n",
    "`, w.HtmlId(), answer))\n",
    "}()\n",
    "\n",
    "// Wait for slider change or timeout.\n",
    "select {\n",
    "case newValue := <-wChan.C:\n",
    "    time.Sleep(100 * time.Millisecond)  // Make sure slider also received the update.\n",
    "    // Checks we receive the new value, and that the slider object also recorded it.\n",
    "    if newValue != answer || w.Value() != answer {\n",
    "        fmt.Printf(\"error: received %d, widget registered %d\", newValue, w.Value())\n",
    "        return\n",
    "    }\n",
    "case <-time.After(500 * time.Millisecond):\n",
    "    fmt.Println(\"error: change timedout\")\n",
    "    return\n",
    "}\n",
    "\n",
    "// SetValue and get it reflected back.\n",
    "w.SetValue(answer+1)\n",
    "time.Sleep(100 * time.Millisecond)\n",
    "dom.TransientJavascript(fmt.Sprintf(`\n",
    "(() => {\n",
    "    let el = document.getElementById(\"%s\");\n",
    "    let gonb_comm = globalThis.gonb_comm;\n",
    "    gonb_comm.send(\"%s\", el.value)\n",
    "})();\n",
    "`, w.HtmlId(), w.Address()))\n",
    "time.Sleep(100 * time.Millisecond)\n",
    "\n",
    "select {\n",
    "case newValue := <-wChan.C:\n",
    "    if newValue != answer+1 || w.Value() != answer+1 {\n",
    "        fmt.Printf(\"error with SetValue: received %d, widget registered %d\", newValue, w.Value())\n",
    "        return\n",
    "    }\n",
    "case <-time.After(5000 * time.Millisecond):\n",
    "    fmt.Println(\"error: SetValue timedout\")\n",
    "    return\n",
    "}\n",
    "\n",
    "fmt.Println(\"widget tested ok\")\n",
    "wChan.Close()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ae7ed0f1-3e0b-47fc-a655-7a181ca23905",
   "metadata": {},
   "source": [
    "## Drop-Down Select\n",
    "\n",
    "We create a `<select>` element, and then interact with it with Javascript and test we get the correct reading."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "81a3573f-cb99-4ccc-a9ce-a69e8fe91d9e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div id=\"dom.transient_div_bd206d18\"></div>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": []
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "widget tested ok\n"
     ]
    }
   ],
   "source": [
    "%%\n",
    "const answer = 3\n",
    "\n",
    "// Setting these 3 values below are not needed, we just do it to test they work.\n",
    "htmlId := \"gonb_widget_test_\" + gonbui.UniqueId()\n",
    "rootId := dom.CreateTransientDiv()\n",
    "address := \"widget/\" + gonbui.UniqueId()\n",
    "\n",
    "options := []string{\"ab\", \"cd\", \"ef\", \"xy\", \"z\"}\n",
    "w := widgets.Select(options).\n",
    "    WithAddress(address).WithHtmlId(htmlId).AppendTo(rootId).Done()\n",
    "\n",
    "wChan := w.Listen().WithBuffer(1)\n",
    "if w.HtmlId() != htmlId {\n",
    "    fmt.Println(\"error: wrong htmlId\")\n",
    "    return\n",
    "}\n",
    "if w.Address() != address {\n",
    "    fmt.Println(\"error: wrong address\")\n",
    "    return\n",
    "}\n",
    "\n",
    "go func() {\n",
    "    time.Sleep(100 * time.Millisecond);\n",
    "    dom.TransientJavascript(fmt.Sprintf(`\n",
    "(() => {\n",
    "    let el = document.getElementById(\"%s\");\n",
    "    const answer = %d;\n",
    "    el.value = answer;  // Doesn't trigger \"change\" event, unfortunately.\n",
    "    el.dispatchEvent(new Event(\"change\"));\n",
    "    console.log(\"Select set to \"+answer);\n",
    "})();\n",
    "`, w.HtmlId(), answer))\n",
    "}()\n",
    "\n",
    "// Wait for slider change or timeout.\n",
    "select {\n",
    "case newValue := <-wChan.C:\n",
    "    time.Sleep(100 * time.Millisecond)  // Make sure slider also received the update.\n",
    "    // Checks we receive the new value, and that the slider object also recorded it.\n",
    "    if newValue != answer || w.Value() != answer {\n",
    "        fmt.Printf(\"error: received %d, widget registered %d\", newValue, w.Value())\n",
    "        return\n",
    "    }\n",
    "case <-time.After(500 * time.Millisecond):\n",
    "    fmt.Println(\"error: change timedout\")\n",
    "    return\n",
    "}\n",
    "\n",
    "// SetValue and get it reflected back.\n",
    "w.SetValue(answer+1)\n",
    "time.Sleep(100 * time.Millisecond)\n",
    "dom.TransientJavascript(fmt.Sprintf(`\n",
    "(() => {\n",
    "    let el = document.getElementById(\"%s\");\n",
    "    let gonb_comm = globalThis.gonb_comm;\n",
    "    gonb_comm.send(\"%s\", el.value)\n",
    "})();\n",
    "`, w.HtmlId(), w.Address()))\n",
    "time.Sleep(100 * time.Millisecond)\n",
    "\n",
    "select {\n",
    "case newValue := <-wChan.C:\n",
    "    if newValue != answer+1 || w.Value() != answer+1 {\n",
    "        fmt.Printf(\"error with SetValue: received %d, widget registered %d\", newValue, w.Value())\n",
    "        return\n",
    "    }\n",
    "case <-time.After(5000 * time.Millisecond):\n",
    "    fmt.Println(\"error: SetValue timedout\")\n",
    "    return\n",
    "}\n",
    "\n",
    "fmt.Println(\"widget tested ok\")\n",
    "wChan.Close()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Go (gonb)",
   "language": "go",
   "name": "gonb"
  },
  "language_info": {
   "codemirror_mode": "",
   "file_extension": ".go",
   "mimetype": "text/x-go",
   "name": "go",
   "nbconvert_exporter": "",
   "pygments_lexer": "",
   "version": "go1.25.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
